package com.miplus.ccrm.core.service.action;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.miplus.ccrm.common.*;
import com.miplus.ccrm.core.controller.vo.action.*;
import com.miplus.ccrm.core.controller.vo.conf.WfNextStepVo;
import com.miplus.ccrm.core.mapper.*;
import com.miplus.ccrm.core.pojo.*;
import com.miplus.ccrm.core.service.ExportService;
import com.miplus.ccrm.core.service.FiWorkFlowCase;
import com.miplus.ccrm.core.service.conf.MsgAlertsService;
import com.miplus.ccrm.core.service.conf.MsgDefineService;
import com.miplus.ccrm.core.service.crd.FiCreditcasesService;
import com.miplus.ccrm.core.service.crd.FiImportHistService;
import com.miplus.ccrm.core.service.crd.FiTaskService;
import com.miplus.ccrm.core.service.strategy.CpCalllistService;
import com.miplus.ccrm.core.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author pc
* @description 针对表【fi_delay(留案申请表)】的数据库操作Service实现
* @createDate 2024-04-08 10:12:10
*/
@Slf4j
@Service
public class FiDelayServiceImpl extends MPJBaseServiceImpl<FiDelayMapper, FiDelay>
    implements FiDelayService{
    @Resource
    private FiTaskService fiTaskService;
    @Resource
    private CpCalllistService cpCalllistService;
    @Resource
    private FiWorkFlowCase fiWorkFlowCase;
    @Resource
    private MsgAlertsService msgAlertsService;

    @Resource
    private MsgDefineService msgDefineService;

    @Resource
    private FiCreditcasesMapper fiCreditcasesMapper;

    @Resource
    private FiCreditcasesHistMapper fiCreditcasesHistMapper;

    @Resource
    private ExportService exportService;

    @Resource
    private FiImportHistService fiImportHistService;

    @Resource
    private WfCaseactionsMapper wfCaseactionsMapper;

    @Override
    public IPage<FiDelayResVo> fiDelayList(String $usrid, FiApplyReqVo paras)
    {
        return selectJoinListPage(new Page<>(paras.getPageNum(), paras.getPageSize()),
                FiDelayResVo.class,
                new MPJLambdaWrapper<FiDelay>()
                        .leftJoin(CpCalllist.class, "cl",CpCalllist::getClid, FiDelay::getClid)
                        .leftJoin(FiCreditcases.class, "fc", FiCreditcases::getCreditcaseid, FiDelay::getFicaseid)
                        .leftJoin("iou_product iup on iup.product_id = fc.product_id and ifnull(iup.merchant_id,'')=ifnull(fc.merchant_id,'')")
                        .leftJoin("wf_cases wc on wc.wfid = cl.wfid and wc.originalid = t.applynumber")
                        .leftJoin("wf_caseactions wca on wca.caseid = wc.caseid and wca.actionid is null")
                        .leftJoin("wf_steps ws on ws.stepid = wca.stepid")
                        .selectAs("fun_getnames('FI.DELAY.STATUS',t.status)", FiDelayResVo::getStatusdsc)
                        .selectAs("fun_getnames('FI.DELAY.REASON',t.applyreason)", FiDelayResVo::getApplyreason)
                        .selectAs(CpCalllist::getWfid, FiDelayResVo::getWfid)
                        .selectAs("(select g.GRPNAME from sys_user u join sys_grp g on u.GROUPS=g.GRPID where u.USRID=t.applyusr)",FiDelayResVo::getApplygrp)
                        .selectAll(FiDelay.class)
                        .selectAs("wca.caseid",FiDelayResVo::getWfcaseid)
                        .selectAs("wca.caseactid",FiDelayResVo::getWfcaseactid)
                        .selectAs("wca.stepid",FiDelayResVo::getWfstepid)
                        .selectAs("ws.name",FiDelayResVo::getWfstepname)
                        .apply("fc_hasApplyView(t.applynumber,'SYS_DELAY',t.applyusr,{0},t.status,t.confirmusr)>0",$usrid)
                        .apply(JPubUtils.IsNotEmpty(paras.getApplygrp()),"fc_hasUsrInGroup(t.applyusr,{0})>0",paras.getApplygrp())
                        .eq(JPubUtils.IsNotEmpty(paras.getFicaseid()), FiDelay::getFicaseid,paras.getFicaseid())
                        .eq(JPubUtils.IsNotEmpty(paras.getStatus()),FiDelay::getStatus,paras.getStatus())
                        .eq(JPubUtils.IsNotEmpty(paras.getApplyusr()),FiDelay::getApplyusr,paras.getApplyusr())
                        .between(JPubUtils.IsNotEmpty(paras.getStartdt()), FiDelay::getApplydt,JPubUtils.StringToDate(paras.getStartdt()),JPubUtils.StringToDate(paras.getEnddt()))
                        .ge(JPubUtils.IsNotEmpty(paras.getOverdueday1()),FiDelay::getMthsDay,paras.getOverdueday1())
                        .le(JPubUtils.IsNotEmpty(paras.getOverdueday2()),FiDelay::getMthsDay,paras.getOverdueday2())
                        .in(JPubUtils.IsNotEmpty(paras.getDisttaglist()),FiDelay::getDisttag,paras.getDisttaglist())
                        .in(JPubUtils.IsNotEmpty(paras.getProductlist()),"iup.id",paras.getProductlist())
                        .ge(JPubUtils.IsNotEmpty(paras.getDelayenddate1()),FiDelay::getDelaycpendtime,paras.getDelayenddate1())
                        .le(JPubUtils.IsNotEmpty(paras.getDelayenddate2()),FiDelay::getDelaycpendtime,paras.getDelayenddate2())
                        .eq(JPubUtils.IsNotEmpty(paras.getStepid()),"wca.stepid",paras.getStepid())
        ).convert(x->{
            if( StringUtils.isEmpty(x.getRecyclemode()) && ObjectUtils.isNotNull(x.getRecycleType()))
            {
                String[] s = new String[5];
                for(int i=0;i<5;i++)
                    s[i] = "0";
                Integer recycleType = x.getRecycleType();
                if(recycleType >= 1000) //停催回收
                    s[4] = "1";
                recycleType = recycleType % 1000;
                if( recycleType >= 300){ //逾期变更回收
                    s[0] = "1";
                    s[1] = "1";
                }
                else if(recycleType >= 200) //降期回收
                {
                    s[1] = "1";
                }
                else if(recycleType >= 100) //升期回收
                {
                    s[0] = "1";
                }
                if(recycleType % 100 >= 10) //逾期结清回收
                {
                    s[2] = "1";
                }
                else if(recycleType % 100 > 0) //借据结清回收
                {
                    s[3] = "1";
                }
                x.setRecyclemode(String.format("%s#%s#%s#%s#%s",s[0],s[1],s[2],s[3],s[4]));
            }
            return x;
        });
    }

    @Override
    @Transactional
    public CmdResVo fiDelayAdd(String $usrid, FiDelayAddVo paras)
    {
        //1=待审核 支持批量审批
        if( paras.getFicaseids() == null || paras.getFicaseids().size() == 0)
        {
            log.info("fiDelayAdd, not find ceditcaseid.");
            return new CmdResVo(-1,"没有指定案件");
        }

        List<FiDelay> fiDelay=selectJoinList(FiDelay.class,
                new MPJLambdaWrapper<FiDelay>()
                        .in(FiDelay::getFicaseid,paras.getFicaseids())
                        .in(FiDelay::getStatus,
                                EnumConfirmStatus.New.value,EnumConfirmStatus.Pending.value,
                                EnumConfirmStatus.Process.value)
        );
        //相同的案件正在审核中
        if(JPubUtils.IsNotEmpty(fiDelay)){
            log.info("fiDelayAdd: 案件：{}正在审批中, 不能重复添加。",fiDelay.get(0).getFicaseid());
            throw new ServiceException(EnumRCode.DataProcess.value, EnumRCode.DataProcess.name);
        }
        CpCalllist cpCalllist = cpCalllistService.getById(paras.getClid());
        if(cpCalllist == null){
            log.info("fiDelayAdd: 未设置队列 clid:<{}>。",paras.getClid());
            return new CmdResVo(-1,"没有设置留案队列。");
        }
        String msgId = cpCalllist.getSendmodel();
        if(JPubUtils.IsEmpty(msgId))
            msgId = cpCalllist.getClid();
        MsgDefine msg = msgDefineService.getById(msgId);
        String conditionSql = "";
        if(msg != null && JPubUtils.IsNotEmpty(msg.getContentsql())){
            conditionSql = msg.getContentsql();
        }
        Integer wfid=cpCalllist.getWfid();
        log.info("fiDelayAdd: clid:<{}>,wfid:<{}>,conditionSql:<{}>",paras.getClid(),wfid,conditionSql);

        //获取条件SQL。
        String cpendtime = JPubUtils.DTimeToString(paras.getCpenddate());
        List<FiCreditcases> fiCreditcasesList = baseMapper.getValidCreditCase(conditionSql,paras.getFicaseids(), cpendtime);
        if( fiCreditcasesList == null || fiCreditcasesList.size() == 0)
        {
            return new CmdResVo(-1,"案件无法留案，不符合留案条件。");
        }
        Map<Integer,FiCreditcases> mvalidCaselist = fiCreditcasesList.stream().collect(Collectors.toMap(item -> item.getCreditcaseid(), item -> item));

        for(Integer caseid : paras.getFicaseids())
        {
            if(mvalidCaselist.get(caseid) == null)
            {
                return new CmdResVo(-1,"案件:" + caseid + "无法留案，不符合留案条件。");
            }
        }
        int ret = 0;
        for(FiCreditcases v : fiCreditcasesList)
        {
            if(v.getCpEndTime().after(paras.getCpenddate()))
            {
                log.info("fiDelayAdd: do not delay. cpendtime:<{}>, delaydate:<{}>",v.getCpEndTime(),paras.getCpenddate());
                continue;
            }
            //CAMPAIGN.CHANNEL=11留案
            int tasknumber = fiTaskService.addTask($usrid, paras.getClid(),v.getCreditcaseid(), EnumFiChannel.Delay.value,null,null,null,
                    "原因：" + paras.getApplyreason() + " 留案备注："+ paras.getApplydsc());

            log.info("applyAdd:clid={},ficase={},tasknumber={},wfid={}", paras.getClid(), v.getCreditcaseid(), tasknumber, wfid);

            FiDelay fi = new FiDelay();
            fi.setClid(paras.getClid());
            if (paras.getDelaydays() != null) {
                fi.setDelaydays(paras.getDelaydays());
            }
            fi.setApplyreason(paras.getApplyreason());
            fi.setFicaseid(v.getCreditcaseid());
            fi.setMthsDay(v.getMthsDay());
            fi.setProductId(v.getProductId());
            fi.setProductName(v.getProductName());
            fi.setCpEndTime(v.getCpEndTime());
            fi.setDisttag(v.getDisttag());
            fi.setRecycleType(v.getRecycleType());
            fi.setApplydsc(paras.getApplydsc());
            fi.setTasknumber(tasknumber);
            fi.setStatus(EnumConfirmStatus.New.value); //新建
            fi.setApplydt(new Date());
            fi.setApplyusr($usrid);
            fi.setDelaycpendtime(paras.getCpenddate());
            save(fi);
            int applynumber = fi.getApplynumber();
            ret++;
            if (wfid > 0) {
                //wfid=0 表示此任务clid没有配置工作流，前台可以根据是否有wfid显示不通的审批流程或状态
                String status = fiWorkFlowCase.doCreate($usrid, wfid, applynumber, tasknumber);
                fi = new FiDelay();
                fi.setApplynumber(applynumber);
                fi.setStatus(Integer.parseInt(status)); //处理中
                baseMapper.updateById(fi);
            }
        }
        return new CmdResVo(ret,"留案申请成功");
    }

    @Override
    public CmdResVo fiDelayCancel(String $usrid, FiApplyCancelVo paras)
    {
        FiDelay fi=baseMapper.selectById(paras.getApplynumber());
        if(JPubUtils.IsNotEmpty(fi.getTasknumber())){
            fiTaskService.updateStatus(fi.getTasknumber(),$usrid,"X");
        }
        else{
            log.warn("applyCancel:getTasknumber.isnull");
        }
        Integer wfid=cpCalllistService.getCalllistWfID(fi.getClid());
        fiWorkFlowCase.doCancel($usrid,wfid,fi.getApplynumber());

        fi = new FiDelay();
        fi.setApplynumber(paras.getApplynumber());
        fi.setStatus(EnumConfirmStatus.Cancel.value); //取消
        fi.setCancelusr($usrid);
        fi.setCanceldt(new Date());
        int ret=baseMapper.updateById(fi);

        return new CmdResVo(ret,fi.getApplynumber().toString());
    }

    @Override
    public void doCancel(String $usrid, List<Integer> ficaseid,int enstatus)
    {//留案申请过程中，如果回收，分案，出催后，自动取消申请
        List<FiDelay> filist=baseMapper.selectList(new MPJLambdaWrapper<FiDelay>()
                .in(FiDelay::getFicaseid,ficaseid)
                .eq(FiDelay::getStatus,enstatus)
        );
        if(JPubUtils.IsEmpty(filist)) return;

        log.info("doCancel({}):Status={} By {}",ficaseid,enstatus,$usrid);

        for (FiDelay fi : filist){
            if(enstatus==EnumConfirmStatus.Pending.value) {
                if (JPubUtils.IsNotEmpty(fi.getTasknumber())) {
                    fiTaskService.updateStatus(fi.getTasknumber(), $usrid, "X");
                }
                Integer wfid = cpCalllistService.getCalllistWfID(fi.getClid());
                fiWorkFlowCase.doCancel($usrid, wfid, fi.getApplynumber());
            }
            fi.setStatus(EnumConfirmStatus.Cancel.value); //取消
            fi.setCancelusr($usrid);
            fi.setCanceldt(new Date());
            baseMapper.updateById(fi);
        }
    }

    /**
     * 批量审批，只是批量审批。
     * @param $usrid
     * @param paras
     * @return
     */
    @Override
    public CmdResVo fiDelayConfirm(String $usrid, FiApplyConfirmVo paras)
    {
        String okstatus=paras.getActionid().toString();
        int ret = 0;
        if( paras.getConfirmList() == null || paras.getConfirmList().size() == 0)
            return new CmdResVo(-1,"没有待审批的数据");
        List<Integer> actionCaseids = paras.getConfirmList().stream().map(ConfirmReqVo::getCaseactid).filter(p->p!=null).collect(Collectors.toList());
        if( actionCaseids.size() > 1 && wfCaseactionsMapper.getStepCount(actionCaseids) > 1)
            return new CmdResVo(-1,"数据中含有多个流程节点，无法批量审批。");
        for( ConfirmReqVo x : paras.getConfirmList()) {
            boolean confirmend=true;
            //FiDelay fiDelay = baseMapper.selectById(paras.getApplynumber());
            FiDelay fiDelay = baseMapper.selectById(x.getApplynumber());
            //if (JPubUtils.IsNotEmpty(paras.getCaseactid())) {
            if (JPubUtils.IsNotEmpty(x.getCaseactid())) {
                //需要走工作流
                WfNextStepVo wfNextStepVo = fiWorkFlowCase.doExec($usrid, x.getCaseactid(),
                        paras.getActionid(), x.getWfcaseid(), paras.getConfirmdsc()
                );
                okstatus = wfNextStepVo.getStatus();
                //可能有多级审核,3=审核全部完成
                if (!JPubUtils.IsEqual(wfNextStepVo.getType(), EnumWorkFlowStep.End.value)) {
                    confirmend = false;
                }
            } else {
                Integer wfid = cpCalllistService.getCalllistWfID(fiDelay.getClid());
                if (wfid == null || wfid == 0) {//不使用工作流,这种情况下okstatus就是字典ID，直接写审核结果
                    log.info("fiDeductConfirm.nouse workflow：okstatus={}", okstatus);
                } else {//可能配置错误或网络引起的，2024-08-29
                    log.warn("fiDeductConfirm.nofind wf caseactid.");
                    throw new ServiceException(EnumRCode.WfDataErr.value, EnumRCode.WfDataErr.name);
                }
            }

            //单纯就是审批流程，结果后续操作就在这里写，如果有后续反馈的，在result里面写，比如外访
            if (JPubUtils.IsNotEmpty(fiDelay.getTasknumber()) && confirmend) {
                //HZbank的留案比较特殊，留案操作不直接增加绑定日期，只做标识，下次分案是分给上次申请留案的组
                if (JPubUtils.IsEqual(okstatus, "2")) {//审核通过

                    FiCreditcases fcase = fiCreditcasesMapper.selectById(fiDelay.getFicaseid());
                    log.info("applyConfirm.Ok:ficaseid={} Bindenddt.adddays={},delaycpenddate={},ccc:{},cpendtime",
                            fiDelay.getFicaseid(), fiDelay.getDelaydays(), fiDelay.getDelaycpendtime(),
                            fcase.getCcc(), fcase.getCpEndTime()
                    );
                    if (fcase != null && fcase.getCcc().equals(EnumFiCaseQueue.Q.value)
                            && fiDelay.getDelaycpendtime().after(fcase.getCpEndTime())) {
                        //Date addday = JPubUtils.addDays(fcase.getBindenddt(), fiDelay.getDelaydays());
                        //fcase.setBindenddt(addday);
                        Integer pRecycleType = fcase.getRecycleType();
                        if (StringUtils.isNotEmpty(paras.getRecycletype())) {

                            String[] s = paras.getRecycletype().split("#");
                            pRecycleType = Integer.parseInt(s[0]) * 100 + Integer.parseInt(s[1]) * 200 + Integer.parseInt(s[2]) * 10 + Integer.parseInt(s[3]);
                            if (s.length >= 5)
                                pRecycleType += Integer.parseInt(s[4]) * 1000;
                        }
                        fcase.setCpEndTime(fiDelay.getDelaycpendtime());
                        fcase.setRecycleType(pRecycleType);
                        fiCreditcasesMapper.updateById(fcase);
                        //更新hist表中的截至数据。
                        fiCreditcasesHistMapper.update(null,
                                new LambdaUpdateWrapper<FiCreditcasesHist>()
                                        .eq(FiCreditcasesHist::getCreditcaseid,fcase.getCreditcaseid())
                                        .eq(FiCreditcasesHist::getDistbatchid,fcase.getDistbatchid())
                                        .set(FiCreditcasesHist::getCpEndTime,fcase.getCpEndTime())
                        );
                        log.info("applyConfirm creditcase update ok:caseid:<{}>,cpendttime:<{}>",
                                fcase.getCreditcaseid(), fcase.getCpEndTime());
                    }
                    fiTaskService.updateStatus(fiDelay.getTasknumber(), $usrid, "R");
                    fiDelay.setRecyclemode(paras.getRecycletype());
                    updateById(fiDelay);
                } else {//审核不通过
                    fiTaskService.updateStatus(fiDelay.getTasknumber(), $usrid, "F");
                }
                fiTaskService.updateStatus(
                        fiDelay.getTasknumber(),
                        $usrid,
                        fiWorkFlowCase.getPassStatus(okstatus)
                );
                msgAlertsService.msgAlertAdd(EnumFiChannel.Delay,
                        fiDelay.getApplyusr(),
                        $usrid, EnumUtil.ConfirmStatusName(okstatus)
                );
            }

            FiDelay fi = new FiDelay();
            fi.setApplynumber(x.getApplynumber());
            fi.setStatus(Integer.parseInt(okstatus));
            fi.setConfirmusr($usrid);
            fi.setConfirmdt(new Date());
            fi.setConfirmdsc(paras.getConfirmdsc());
            fi.setRecyclemode(paras.getRecycletype());
            baseMapper.updateById(fi);
            ret++;
        }
        //return new CmdResVo(ret,paras.getApplynumber().toString());
        return new CmdResVo(ret,"审批完成");
    }

    @Override
    @Transactional
    public CmdResVo fiDelayListAdd(String $usrid, DelayListAddVo paras)
    {
        String clid=paras.getClid();
        if(JPubUtils.IsEmpty(clid)){
            clid="SYS_DELAY";
        }
        Integer wfid=cpCalllistService.getCalllistWfID(clid);
        int ret = 0;
        for(Integer ficaseid : paras.getFicaseList()) {
            FiDelay fiDelay=selectJoinOne(FiDelay.class,
                    new MPJLambdaWrapper<FiDelay>()
                            .eq(FiDelay::getFicaseid,ficaseid)
                            .in(FiDelay::getStatus,
                                    EnumConfirmStatus.New.value,EnumConfirmStatus.Pending.value,
                                    EnumConfirmStatus.Process.value,EnumConfirmStatus.Pass.value)
            );

            if(fiDelay!=null){
                log.info("fiDelayListAdd:ficase={} existdelay", ficaseid);
                continue;
            }
            int tasknumber = fiTaskService.addTask($usrid, clid, ficaseid, EnumFiChannel.Delay.value);

            log.info("fiDelayListAdd:clid={},ficase={},tasknumber={},wfid={}", clid, ficaseid, tasknumber, wfid);

            FiDelay fi = new FiDelay();
            fi.setClid(clid);
            if (paras.getDelaydays() != null) {
                fi.setDelaydays(paras.getDelaydays());
            }
            fi.setApplyreason(paras.getReason());
            fi.setFicaseid(ficaseid);
            fi.setApplydsc(paras.getApplydsc());
            fi.setTasknumber(tasknumber);
            fi.setStatus(EnumConfirmStatus.New.value); //新建
            fi.setApplydt(new Date());
            fi.setApplyusr($usrid);
            ret = baseMapper.insert(fi);

            int applynumber = fi.getApplynumber();

            if (wfid > 0) {
                //wfid=0 表示此任务clid没有配置工作流，前台可以根据是否有wfid显示不通的审批流程或状态
                String status = fiWorkFlowCase.doCreate($usrid, wfid, applynumber, tasknumber);
                fi = new FiDelay();
                fi.setApplynumber(applynumber);
                fi.setStatus(Integer.parseInt(status)); //处理中
                baseMapper.updateById(fi);
            }
            ret++;
        }

        return new CmdResVo(ret);
    }

    @Override
    public CmdResVo expDelay(JSysUser user, FiApplyReqVo paras, HttpServletResponse response)
    {
        String $usrid=user.getUsrid();

        IPage<FiDelayResVo> pages=fiDelayList($usrid, paras);
        ArrayList arr=new ArrayList();

        String exptempid=JPubUtils.getExpTempID(paras.getExpfilename(),"留案列表");
        String filename=JPubUtils.getExpFileName(
                paras.getExpfilename(),exptempid
        );
        pages.convert(u->{
            HashMap map=JPubUtils.toExportMap(u);
            if(map!=null) arr.add(map);
            return u;
        });
        CmdResVo resVo=exportService.ToExcel(
                response,
                arr,
                paras.getExpidlist(),
                paras.getExpnamelist(),
                filename,
                $usrid
        );

        int okcount=resVo.getCount();
        if(okcount>0){
            fiImportHistService.AddExportHist(
                    resVo.getKeyId(),
                    $usrid,
                    exptempid,
                    filename,
                    okcount
            );
        }

        return resVo;
    }
}




